/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


/*************************************************************************
 *\author: starlove
 *************************************************************************/
#ifndef __CC_PYTHON_IMPORT_O_NODES__
#define __CC_PYTHON_IMPORT_O_NODES__

#include <cocos2d.h>
#include <python_import_utils.hpp>

#define __schedule_selector(_SELECTOR) (cocos2d::SEL_SCHEDULE)(&_SELECTOR)
#define __callfuncND_selector(_SELECTOR) (cocos2d::SEL_CallFuncND)(&_SELECTOR)
#define __callfuncO_selector(_SELECTOR) (cocos2d::SEL_CallFuncO)(&_SELECTOR)
#define __menu_selector(_SELECTOR) (cocos2d::SEL_MenuHandler)(&_SELECTOR)

NS_CCPY_BEGIN
	CCNODE_OBJECT_WRAPPER_DEF(CCNode)
	CCNODE_OBJECT_WRAPPER_DEF(CCNodeRGBA)
	CCNODE_OBJECT_WRAPPER_DEF(CCScene)
	
	typedef struct __PY_CALLFUNC_CB_INFO
	{
		__PY_CALLFUNC_CB_INFO()
		{
			pyFunc = NULL;
			data = NULL;
			need_current = false;
			need_param = false;
		}
		~__PY_CALLFUNC_CB_INFO()
		{
			if(pyFunc)
				Py_SAFE_DECREF(pyFunc);
			if(data)
				Py_SAFE_DECREF(data);
			CCLog("[DELETE]PY_CALLFUNC_CB_INFO pyFunc ref: %d, data ref: %d", pyFunc ? pyFunc->ob_refcnt : -1, data ? data->ob_refcnt : -1);
		}
		PyObject *pyFunc;
		PyObject *data;
		bool need_current;
		bool need_param;
	}PY_CALLFUNC_CB_INFO, *LPY_CALLFUNC_CB_INFO;

	class CallFuncDelegate : public cocos2d::CCObject
	{
	public:
		CallFuncDelegate(PyObject *fn = NULL){ m_fn = fn; };
		static CallFuncDelegate *share();
		static CallFuncDelegate *create(PyObject *fn);
		void release(){ delete this; };
		void On_SEL_CallFuncND(cocos2d::CCNode* N, void* P);
		void On_SEL_CallFuncO(cocos2d::CCObject* O);
	private:
		PyObject *m_fn;
	};

	class CPyPointer : public CPyClassBase
	{
	PYCLASS_DEF_INTERFACE(CPyPointer)
	public:
		static CPyPointer *create(void *v, unsigned int size = 0)
		{
			CPyPointer *pyv = (CPyPointer *)PyObject_New(CPyPointer, CPyClassBase::GetType<CPyPointer>());
			if(!pyv)
				return NULL;
			pyv->setPtr(v);
			pyv->setSize(size);
			return pyv;
		}
		void *getPtr(){ return m_ptr; };
		unsigned int getSize(){ return m_size; };
		void setPtr(void *o){ m_ptr = o; };
		void setSize(unsigned int size){ m_size = size; };
		PYCLASS_GETSET_MAPPING_DEF(CPyPointer, ptr);
		PYCLASS_GETSET_MAPPING_DEF(CPyPointer, size);
	protected:
		void *m_ptr;
		unsigned int m_size;
	};

	class CPyAcceleration : public CPyClassBase
	{
	PYCLASS_DEF_INTERFACE(CPyAcceleration)
	public:
		static CPyAcceleration *create(cocos2d::CCAcceleration *v)
		{
			CPyAcceleration *pyv = (CPyAcceleration *)PyObject_New(CPyAcceleration, CPyClassBase::GetType<CPyAcceleration>());
			if(!pyv)
				return NULL;
			pyv->setCCObject(v);
			return pyv;
		}
		cocos2d::CCAcceleration *getCCObject(){ return m_ccobj; };
		void setCCObject(cocos2d::CCAcceleration *o){ m_ccobj = o; };

		PYCLASS_GETSET_MAPPING_DEF(CPyAcceleration, x);
		PYCLASS_GETSET_MAPPING_DEF(CPyAcceleration, y);
		PYCLASS_GETSET_MAPPING_DEF(CPyAcceleration, z);
		PYCLASS_GETSET_MAPPING_DEF(CPyAcceleration, timestamp);

	protected:
		cocos2d::CCAcceleration *m_ccobj;
	};


	class CPyCCObject : public CPyClassBase
	{
	PYCLASS_DEF_INTERFACE(CPyCCObject)
	public:
		static PyObject *create_object(PyTypeObject *pt, cocos2d::CCObject *v)
		{
			CPyCCObject *pyv = (CPyCCObject *)_PyObject_New(pt);
			if(!pyv)
				return NULL;
			pyv->setCCObject(v);
			return (PyObject *)pyv;
		}
		template <typename T>
		static T *create(cocos2d::CCObject *v)
		{
			T *pyv = (T *)PyObject_New(T, CPyClassBase::GetType<T>());
			if(!pyv)
				return NULL;
			pyv->setCCObject(v);
			return pyv;
		}
		CPyCCObject(){};
		cocos2d::CCObject *getCCObject(){ return m_ccobj; };
		void setCCObject(cocos2d::CCObject *o){ m_ccobj = o; };
		PYCLASS_METHOD_DEF(CPyCCObject,typeto);
		PYCLASS_METHOD_DEF(CPyCCObject,release);
		PYCLASS_METHOD_DEF(CPyCCObject,retain);
		PYCLASS_METHOD_DEF(CPyCCObject,autorelease);
		PYCLASS_METHOD_DEF(CPyCCObject,copy);
		PYCLASS_METHOD_DEF(CPyCCObject,isSingleReference);
		PYCLASS_METHOD_DEF(CPyCCObject,retainCount);
		PYCLASS_METHOD_DEF(CPyCCObject,isEqual);
		PYCLASS_METHOD_DEF(CPyCCObject,update);
	protected:
		cocos2d::CCObject *m_ccobj;
	};

	class CPyNode : public CPyCCObject
	{
	PYCLASS_DEF_INTERFACE(CPyNode)
	public:
		CPyNode();
		PYCLASS_GET_DEF(CPyNode, description);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ZOrder);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,VertexZ);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ScaleX);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ScaleY);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Scale);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Position);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,PositionX);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,PositionY);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,SkewX);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,SkewY);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,AnchorPoint);
		PYCLASS_GET_DEF(CPyNode,AnchorPointInPoints);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ContentSize);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Visible);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Rotation);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,RotationX);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,RotationY);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,OrderOfArrival);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,GLServerState);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ignoreAnchorPointForPosition);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Parent);
		PYCLASS_GET_DEF(CPyNode,Children);
		PYCLASS_GET_DEF(CPyNode,ChildrenCount);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Grid);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Tag);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,UserData);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,UserObject);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ShaderProgram);
		PYCLASS_GET_DEF(CPyNode,Camera);
		PYCLASS_GET_DEF(CPyNode,isRunning);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,ActionManager);
		PYCLASS_GET_DEF(CPyNode,numberOfRunningActions);
		PYCLASS_GETSET_MAPPING_DEF(CPyNode,Scheduler);
		PYCLASS_GET_DEF(CPyNode,boundingBox);
		PYCLASS_METHOD_DEF(CPyNode,getChildByTag);
		PYCLASS_METHOD_DEF(CPyNode,removeFromParent);
		PYCLASS_METHOD_DEF(CPyNode,removeFromParentAndCleanup);
		PYCLASS_METHOD_KW_DEF(CPyNode,addChild);
		PYCLASS_METHOD_KW_DEF(CPyNode,removeChild);
		PYCLASS_METHOD_KW_DEF(CPyNode,removeChildByTag);
		PYCLASS_METHOD_DEF(CPyNode,removeAllChildren);
		PYCLASS_METHOD_DEF(CPyNode,removeAllChildrenWithCleanup);
		PYCLASS_METHOD_DEF(CPyNode,reorderChild);
		PYCLASS_METHOD_DEF(CPyNode,sortAllChildren);
		PYCLASS_METHOD_DEF(CPyNode,onEnter);
		PYCLASS_METHOD_DEF(CPyNode,onEnterTransitionDidFinish);
		PYCLASS_METHOD_DEF(CPyNode,onExit);
		PYCLASS_METHOD_DEF(CPyNode,onExitTransitionDidStart);
		PYCLASS_METHOD_DEF(CPyNode,cleanup);
		PYCLASS_METHOD_DEF(CPyNode,draw);
		PYCLASS_METHOD_DEF(CPyNode,visit);
		PYCLASS_METHOD_DEF(CPyNode,runAction);
		PYCLASS_METHOD_DEF(CPyNode,stopAllActions);
		PYCLASS_METHOD_DEF(CPyNode,stopAction);
		PYCLASS_METHOD_DEF(CPyNode,stopActionByTag);
		PYCLASS_METHOD_DEF(CPyNode,getActionByTag);
		PYCLASS_METHOD_DEF(CPyNode,isScheduled);
		PYCLASS_METHOD_DEF(CPyNode,scheduleUpdate);
		PYCLASS_METHOD_DEF(CPyNode,scheduleUpdateWithPriority);
		PYCLASS_METHOD_DEF(CPyNode,unscheduleUpdate);
		PYCLASS_METHOD_DEF(CPyNode,schedule);
		PYCLASS_METHOD_DEF(CPyNode,scheduleOnce);
		PYCLASS_METHOD_DEF(CPyNode,unschedule);
		PYCLASS_METHOD_DEF(CPyNode,unscheduleAllSelectors);
		PYCLASS_METHOD_DEF(CPyNode,resumeSchedulerAndActions);
		PYCLASS_METHOD_DEF(CPyNode,pauseSchedulerAndActions);
		PYCLASS_METHOD_DEF(CPyNode,transform);
		PYCLASS_METHOD_DEF(CPyNode,transformAncestors);
		PYCLASS_METHOD_DEF(CPyNode,updateTransform);
		PYCLASS_METHOD_DEF(CPyNode,nodeToParentTransform);
		PYCLASS_METHOD_DEF(CPyNode,parentToNodeTransform);
		PYCLASS_METHOD_DEF(CPyNode,nodeToWorldTransform);
		PYCLASS_METHOD_DEF(CPyNode,worldToNodeTransform);
		PYCLASS_METHOD_DEF(CPyNode,convertToNodeSpace);
		PYCLASS_METHOD_DEF(CPyNode,convertToWorldSpace);
		PYCLASS_METHOD_DEF(CPyNode,convertToNodeSpaceAR);
		PYCLASS_METHOD_DEF(CPyNode,convertToWorldSpaceAR);
		PYCLASS_METHOD_DEF(CPyNode,convertTouchToNodeSpace);
		PYCLASS_METHOD_DEF(CPyNode,convertTouchToNodeSpaceAR);
		PYCLASS_METHOD_DEF(CPyNode,setAdditionalTransform);
	};


	class CPyNodeRGBA : public CPyCCObject
	{
	PYCLASS_DEF_INTERFACE(CPyNodeRGBA)
	public:
		CPyNodeRGBA();
		//CCRGBAPROTOCOL_METHOD_WRAPPER_DEF(CPyNodeRGBA);
	};

	class CPyScene : public CPyCCObject
	{
	PYCLASS_DEF_INTERFACE(CPyScene)
	public:
		CPyScene();
	};

	CC_STRUCT_DEF(CPyPoint, cocos2d::CCPoint)
	PYCLASS_GETSET_MAPPING_DEF(CPyPoint, x);
	PYCLASS_GETSET_MAPPING_DEF(CPyPoint, y);
	CC_STRUCT_DEF_END
	
	CC_STRUCT_DEF(CPySize, cocos2d::CCSize);
	PYCLASS_GETSET_MAPPING_DEF(CPySize, width);
	PYCLASS_GETSET_MAPPING_DEF(CPySize, height);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyRect, cocos2d::CCRect);
	PYCLASS_GETSET_MAPPING_DEF(CPyRect, origin);
	PYCLASS_GETSET_MAPPING_DEF(CPyRect, size);
	CC_STRUCT_DEF_END
		
	CC_STRUCT_DEF(CPyColor3B, cocos2d::ccColor3B);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor3B, r);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor3B, g);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor3B, b);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyColor4B, cocos2d::ccColor4B);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4B, r);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4B, g);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4B, b);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4B, a);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyColor4F, cocos2d::ccColor4F);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4F, r);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4F, g);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4F, b);
	PYCLASS_GETSET_MAPPING_DEF(CPyColor4F, a);
	CC_STRUCT_DEF_END
		
	CC_STRUCT_DEF(CPyVertex2F, cocos2d::ccVertex2F);
	PYCLASS_GETSET_MAPPING_DEF(CPyVertex2F, x);
	PYCLASS_GETSET_MAPPING_DEF(CPyVertex2F, y);
	CC_STRUCT_DEF_END
				
	CC_STRUCT_DEF(CPyVertex3F, cocos2d::ccVertex3F);
	PYCLASS_GETSET_MAPPING_DEF(CPyVertex3F, x);
	PYCLASS_GETSET_MAPPING_DEF(CPyVertex3F, y);
	PYCLASS_GETSET_MAPPING_DEF(CPyVertex3F, z);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyTex2F, cocos2d::ccTex2F);
	PYCLASS_GETSET_MAPPING_DEF(CPyTex2F, u);
	PYCLASS_GETSET_MAPPING_DEF(CPyTex2F, v);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyBlendFunc, cocos2d::ccBlendFunc);
	PYCLASS_GETSET_MAPPING_DEF(CPyBlendFunc, src);
	PYCLASS_GETSET_MAPPING_DEF(CPyBlendFunc, dst);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyTexParams, cocos2d::ccTexParams);
	PYCLASS_GETSET_MAPPING_DEF(CPyTexParams, minFilter);
	PYCLASS_GETSET_MAPPING_DEF(CPyTexParams, magFilter);
	PYCLASS_GETSET_MAPPING_DEF(CPyTexParams, wrapS);
	PYCLASS_GETSET_MAPPING_DEF(CPyTexParams, wrapT);
	CC_STRUCT_DEF_END

	CC_STRUCT_DEF(CPyPointSprite, cocos2d::ccPointSprite);
	PYCLASS_GETSET_MAPPING_DEF(CPyPointSprite, pos);
	PYCLASS_GETSET_MAPPING_DEF(CPyPointSprite, color);
	PYCLASS_GETSET_MAPPING_DEF(CPyPointSprite, size);
	CC_STRUCT_DEF_END
		
	CC_QUAD_DEF(Quad2);
	CC_QUAD_DEF(Quad3);

	CC_VERTEXPOINT_DEF(V2F_C4B_T2F);
	CC_VERTEXPOINT_DEF(V2F_C4F_T2F);
	CC_VERTEXPOINT_DEF(V3F_C4B_T2F);

	CC_STRUCT_DEF(CPyV2F_C4B_T2F_Triangle, cocos2d::ccV2F_C4B_T2F_Triangle);
	PYCLASS_GETSET_MAPPING_DEF(CPyV2F_C4B_T2F_Triangle, a);
	PYCLASS_GETSET_MAPPING_DEF(CPyV2F_C4B_T2F_Triangle, b);
	PYCLASS_GETSET_MAPPING_DEF(CPyV2F_C4B_T2F_Triangle, c);
	CC_STRUCT_DEF_END

	CC_QUAD_DEF(V2F_C4B_T2F_Quad);
	CC_QUAD_DEF(V3F_C4B_T2F_Quad);
	CC_QUAD_DEF(V2F_C4F_T2F_Quad);
	CC_QUAD_DEF(T2F_Quad);

	CC_STRUCT_DEF(CPyAnimationFrameData, cocos2d::ccAnimationFrameData);
	PYCLASS_GETSET_MAPPING_DEF(CPyAnimationFrameData, texCoords);
	PYCLASS_GETSET_MAPPING_DEF(CPyAnimationFrameData, delay);
	PYCLASS_GETSET_MAPPING_DEF(CPyAnimationFrameData, size);
	CC_STRUCT_DEF_END
	
	class CCPyDataConvert
	{
	public:
		static PyObject *CCArrayToPyList(cocos2d::CCArray *v)
		{
			unsigned int nl = v ? v->count() : 0;
			PyObject *pyList = PyList_New(nl);
			for(unsigned int i = 0; i < nl; i ++)
			{
				cocos2d::CCObject *o = v->objectAtIndex(i);
				if(!o)
				{
					PyList_Append(pyList, Py_None);
				}
				else
				{
					IWrapper *wrap = dynamic_cast<IWrapper *>(o);
					if(wrap)
					{
						PyList_Append(pyList, CPyCCObject::create_object(wrap->GetPythonType(), o));
					}else
						PyList_Append(pyList, (PyObject *)CPyCCObject::create<CPyCCObject>(o));
				}
			}
			return pyList;
		}

		static cocos2d::CCArray *CCArrayFromPyList(PyObject *v)
		{
			cocos2d::CCArray *arr = cocos2d::CCArray::create();
			if(!v || !PySequence_Check(v))
				return arr;
			Py_ssize_t s = PySequence_Size(v);
			for(Py_ssize_t i = 0; i < s; i ++)
			{
				PyObject *py = PySequence_GetItem(v, i);
				if(!PyObject_TypeCheck(py, CPyClassBase::GetTypeObject<CPyCCObject>()))
					continue;
				CPyCCObject *act = (CPyCCObject *)py;
				arr->addObject(act->getCCObject());
				Py_SAFE_DECREF(py);
			}
			return arr;
		}

		static PyObject *CCIntArrayToPyList(cocos2d::CCArray *v)
		{
			unsigned int nl = v ? v->count() : 0;
			PyObject *pyList = PyList_New(nl);
			for(unsigned int i = 0; i < nl; i ++)
			{
				cocos2d::CCInteger *o = (cocos2d::CCInteger *)v->objectAtIndex(i);
				if(!o)
				{
					PyList_Append(pyList, Py_None);
				}
				else
				{
					PyList_Append(pyList, Py_BuildValue("i",o->getValue()));
				}
			}
			return pyList;
		}

		static cocos2d::CCArray *CCIntArrayFromPyList(PyObject *v)
		{
			cocos2d::CCArray *arr = cocos2d::CCArray::create();
			if(!v || !PySequence_Check(v))
				return arr;
			Py_ssize_t s = PySequence_Size(v);
			for(Py_ssize_t i = 0; i < s; i ++)
			{
				PyObject *py = PySequence_GetItem(v, i);
				cocos2d::CCInteger *io = cocos2d::CCInteger::create(PyLong_AsLong(py));
				arr->addObject(io);
				Py_SAFE_DECREF(py);
			}
			return arr;
		}

		static cocos2d::CCSet *CCSetFromPyList(PyObject *v)
		{
			cocos2d::CCSet *arr = cocos2d::CCSet::create();
			if(!v || !PySequence_Check(v))
				return arr;
			Py_ssize_t s = PySequence_Size(v);
			for(Py_ssize_t i = 0; i < s; i ++)
			{
				PyObject *py = PySequence_GetItem(v, i);
				//Py_SAFE_DECREF(py);
				if(!PyObject_TypeCheck(py, CPyClassBase::GetTypeObject<CPyCCObject>()))
					continue;
				CPyCCObject *act = (CPyCCObject *)py;
				arr->addObject(act->getCCObject());
				Py_SAFE_DECREF(py);
			}
			return arr;
		}
	};
NS_CCPY_END

#endif